package com.example.spiders;

import com.example.spiders.dto.Picture;
import com.example.utils.HeaderUtils;
import com.example.utils.HttpClientUtils;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;

/**
 * @description: 图片爬虫
 * @author swain
 * @date 2020/12/2
 */
public class PictureSpider {


    private CloseableHttpClient httpClient;
    private String referer;
    private String rootPath;
    private String filePath;

    public PictureSpider() {
        httpClient = HttpClientUtils.getHttpClient();
    }

    /**
     * 开始爬虫爬取！
     *
     * 从爬虫队列的第一条开始，依次爬取每一条url。
     *
     * 分页爬取：爬10页
     * 每个url属于一个分类，每个分类一个文件夹
     * */
    public void start(List<String> urlList) {
        urlList.stream().forEach(url->{
            this.referer = url;

            String dirName = url.substring(22, url.length()-1);  //根据标题名字去创建目录
            //创建分类目录
            File path = new File("F:/picture", dirName); //硬编码路径，需要用户自己指定一个
            if (!path.exists()) {
                path.mkdirs();
                rootPath = path.toString();
            }

            for (int i = 1; i <= 10; i++) {  //分页获取图片数据，简单获取几页就行了
                this.getPage(url + "page/"+ 1);
            }
        });
    }


    /**
     * 标题分页获取链接
     * */
    public void getPage(String url) {
        System.out.println("url：" + url);
        String html = this.getHtml(url);   //获取页面数据
        Map<String, String> picMap = this.extractTitleUrl(html);  //抽取图片的url

        if (picMap == null) {
            return ;
        }
        //获取标题对应的图片页面数据
        this.getPictureHtml(picMap);
    }

    private String getHtml(String url) {
        String html = null;
        HttpGet get = new HttpGet(url);
        //"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3100.0 Safari/537.36"
        get.setHeader("User-Agent", HeaderUtils.getRandomHeader());
        get.setHeader("referer", url);
        try (CloseableHttpResponse response = httpClient.execute(get)) {
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    html = EntityUtils.toString(entity, "UTf-8");   //关闭实体？
                }
            }
            else {
                System.out.println(statusCode);
            }
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return html;
    }

    private Map<String, String> extractTitleUrl(String html) {
        if (html == null) {
            return null;
        }
        Document doc = Jsoup.parse(html, "UTF-8");
        Elements pictures = doc.select("ul#pins > li");

        //不知为何，无法直接获取 a[0]，我不太懂这方面的知识。
        //那我就多处理一步，这里先放下。
        Elements pictureA = pictures.stream()
                .map(pic->pic.getElementsByTag("a").first())
                .collect(Collectors.toCollection(Elements::new));

        return pictureA.stream().collect(Collectors.toMap(
                pic->pic.getElementsByTag("img").first().attr("alt"),
                pic->pic.attr("href")));
    }

    /**
     * 进入每一个标题的链接，再次分页获取图片的链接
     * */
    private void getPictureHtml(Map<String, String> picMap) {
        //进入标题页，在标题页中再次分页下载。
        picMap.forEach((title, url)->{
            //分页下载一个系列的图片，每个系列一个文件夹。
            File dir = new File(rootPath, title.trim());
            if (!dir.exists()) {
                dir.mkdir();
                filePath = dir.toString();  //这个 filePath 是每一个系列图片的文件夹
            }

            for (int i = 1; i <= 60; i++) {
                String html = this.getHtml(url + "/" + i);
                if (html == null) {
                    //每个系列的图片一般没有那么多，
                    //如果返回的页面数据为 null，那就退出这个系列的下载。
                    return ;
                }
                Picture picture = this.extractPictureUrl(html);
                System.out.println("开始下载");
                //多线程实在是太快了（快并不是好事，我改成单线程爬取吧）

                SinglePictureDownloader downloader = new SinglePictureDownloader(picture, referer, filePath);
                downloader.download();
                try {
                    Thread.sleep(1500);   //不要爬的太快了，这里只是学习爬虫的知识。不要扰乱别人的正常服务。
                    System.out.println("爬取完一张图片，休息1.5秒。");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * 获取每一页图片的标题和链接
     * */
    private Picture extractPictureUrl(String html) {
        Document doc = Jsoup.parse(html, "UTF-8");
        //获取标题作为文件名
        String title = doc.getElementsByTag("h2")
                .first()
                .text();

        //获取图片的链接（img 标签的 src 属性）
        String url = doc.getElementsByAttributeValue("class", "main-image")
                .first()
                .getElementsByTag("img")
                .attr("src");

        //获取图片的文件扩展名
        title = title + url.substring(url.lastIndexOf("."));
        return new Picture(title, url);
    }


    public static void main(String[] args) {

        //反爬措施：UA、refer 简单绕过就行了。
        //refer   https://www.mzitu.com/xinggan/

        //爬虫队列
        String[] urls = new String[] {
//                "https://www.mzitu.com/xinggan/",
                "https://www.mzitu.com/japan/",
                "https://www.mzitu.com/taiwan/",
                "https://www.mzitu.com/mm/",
                "https://www.mzitu.com/zipai/"
        };
        List<String> urlList = new ArrayList<>(Arrays.asList(urls));

        PictureSpider spider = new PictureSpider();
        spider.start(urlList);
    }
}
